package cn.sunxiansheng.redis.utils;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

/**
 * Description: Redis的ZSet类型操作
 *
 * @Author sun
 * @Create 2024/11/14 14:50
 * @Version 1.0
 */
public class RSortedSet extends RBase {

    /**
     * 构造器给父类注入RedisTemplate
     *
     * @param redisTemplate RedisTemplate实例
     */
    public RSortedSet(RedisTemplate<String, Object> redisTemplate) {
        super(redisTemplate);
    }

    /**
     * 添加一个元素，并设置分数
     *
     * @param key   键
     * @param data  元素
     * @param score 分数
     * @return 是否添加成功，成功返回true
     */
    public Boolean add(String key, Object data, double score) {
        return redisTemplate.opsForZSet().add(key, data, score);
    }

    /**
     * 自定义的数据分数类型，用于批量添加元素
     *
     * @param <T> 元素的类型
     */
    public static class DataScore<T> {
        /**
         * 数据
         */
        private T data;
        /**
         * 分数
         */
        private Double score;

        public DataScore() {
        }

        public DataScore(T data, Double score) {
            this.data = data;
            this.score = score;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        public Double getScore() {
            return score;
        }

        public void setScore(Double score) {
            this.score = score;
        }
    }

    /**
     * 批量添加元素，并设置分数
     *
     * @param key        键
     * @param dataScores 自定义数据结构集合，包含元素及分数
     * @param <T>        元素的类型
     * @return 添加成功的元素数量
     */
    public <T> Long addBatch(String key, Set<DataScore<T>> dataScores) {
        // 将 Set<DataScore<T>> 转换为 Set<ZSetOperations.TypedTuple<Object>>
        Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
        for (DataScore<T> dataScore : dataScores) {
            tuples.add(new ZSetOperations.TypedTuple<Object>() {
                @Override
                public Object getValue() {
                    return dataScore.getData(); // 调用 getData 方法
                }

                @Override
                public Double getScore() {
                    return dataScore.getScore();
                }

                /**
                 * 比较器，从大到小排序
                 */
                @Override
                public int compareTo(ZSetOperations.TypedTuple<Object> o) {
                    return Double.compare(o.getScore(), this.getScore());
                }
            });
        }

        // 添加到 Redis 的 ZSet
        return redisTemplate.opsForZSet().add(key, tuples);
    }

    /**
     * 移除特定的元素
     *
     * @param key     键
     * @param dataSet 要移除的元素集合
     * @param <T>     元素类型
     * @return 移除成功的元素数量
     */
    public <T> Long remove(String key, Set<T> dataSet) {
        return redisTemplate.opsForZSet().remove(key, dataSet.toArray());
    }

    /**
     * 删除指定分数范围的元素
     *
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @return 删除成功的元素数量
     */
    public Long removeRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 根据delta值调整指定元素的分数
     *
     * @param key   键
     * @param data  元素
     * @param delta 增加或减少的分数值
     * @return 调整后的分数
     */
    public Double changeByDelta(String key, Object data, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, data, delta);
    }

    /**
     * 获取全部ZSet元素（分数从大到小），并将值转换为指定类型
     *
     * @param key  Redis键
     * @param type 元素的目标类型
     * @param <T>  元素类型
     * @return 转换后的ZSet集合，包含分值和对应的值
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> reverseRangeWithScores(String key, Class<T> type) {
        // 获取 Redis 中的 ZSet 数据
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, -1);
        // 使用 TreeSet 并提供自定义比较器，从大到小排序
        Set<ZSetOperations.TypedTuple<T>> result = new TreeSet<>((a, b) -> Double.compare(b.getScore(), a.getScore()));

        // 遍历并进行类型转换
        if (typedTuples != null) {
            for (ZSetOperations.TypedTuple<Object> tuple : typedTuples) {
                T value = convertValue(tuple.getValue(), type);
                Double score = tuple.getScore();
                result.add(new ZSetOperations.TypedTuple<T>() {
                    @Override
                    public T getValue() {
                        return value;
                    }

                    @Override
                    public Double getScore() {
                        return score;
                    }

                    /**
                     * 比较器，从大到小排序
                     */
                    @Override
                    public int compareTo(ZSetOperations.TypedTuple<T> o) {
                        return Double.compare(o.getScore(), this.getScore());
                    }
                });
            }
        }
        return result;
    }

    /**
     * 获取指定元素的分数
     *
     * @param key  键
     * @param data 元素
     * @return 元素的分数
     */
    public Double score(String key, Object data) {
        return redisTemplate.opsForZSet().score(key, data);
    }

    /**
     * 获取指定元素的排名（从大到小）
     *
     * @param key  键
     * @param data 元素
     * @return 排名（从0开始）
     */
    public Long reverseRank(String key, Object data) {
        return redisTemplate.boundZSetOps(key).reverseRank(data);
    }

    /**
     * 获取ZSet中的元素总数
     *
     * @param key 键
     * @return 元素个数
     */
    public Long zCard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 获取指定分数范围内的元素数量
     *
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @return 元素数量
     */
    public Long count(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }
}